<?php
/**
 * Classe de base regroupe les fonctions utiles a toutes les classes (constructeur, get, set...)
 */

// Class
abstract    class       ClassBase {

    // Variables Statiques
    protected static    $_tbl_name      = 'nom_de_la_table';
    protected static    $_prefix        = 'prefix_de_la_table';
    protected static    $_pk_name       = 'primary_key_de_la_table';

    // Propriétés privées
    protected           $_db            = NULL;
    protected           $_datas         = array();
    protected           $_isSelected    = FALSE;

    // Constructeur
    public function     __construct($db, $id = -1, $fields = NULL) {

        // On regarde les erreurs.
		if (!($db instanceof Class_SQL))
            throw new Class_Exception( 'The link to the database is not valid' );

		if ($id == NULL)
			throw new Class_Exception( 'The given datas are not valid in ('.self::$_tbl_name.')' );

		$this->_db = $db;

        if ($id != -1)
            $this->init($id, $fields);

	}

    public function     save() {

        // On regarde si notre object a été construit a partir d'une requete SELECT
        if ($this->_isSelected)
            // oui >    On fait un update
            return $this->update();
        else
            // non >    On crée une nouvelle entré dans la base
            return $this->insert();
    }
    public function     delete() {

        // Variables
        $db = $this->_db;
        $data = $this->_datas;

        // Check erreur
        if (!$this->_isSelected)
            throw new Class_Exception('You can\'t Delete like this');

        // On forme la requete
        $sql = 'DELETE FROM ' . self::$_tbl_name . ' WHERE ' . self::$_pk_name . ' = ?';
        $query = $db->query($sql, $data[self::$_pk_name]);

        // On vérifie le retour de la requete
        if (!$query) {

            // Une foreign key bloque la suppression.
            if ($db->errorCode() == '23000')
                return 'Supprimer cet élément est bloqué par une clé étrang?re.';

        }

        // On retourne un SUCCESS
        return TRUE;
    }

    // Methodes private
    protected function  init($id, $fields = NULL) {

        // On formate les champs a choisir. (Si aucun n'est définie, on prend tout)
        $fields_str = ($fields == NULL) ? '*' : NULL;

        // On prépare la liste des champs
        if ($fields != NULL && $fields != '*') {
            foreach ($fields as $key => $tab) {

                $tmp = self::$_tbl_name . '_' . $val;

                if ($fields_str != NULL)
                    $fields_str .= ', ' . $tmp;
                else
                    $fields_str .= $tmp;
            }
        }

        // On forma la requete
        $req = 'SELECT ' . $fields_str . ' FROM ' . self::$_tbl_name . ' WHERE ' . self::$_pk_name . ' = ? LIMIT 1';
        $this->_datas = $this->_db->fetchRow($req, $id);

        // On vérifie que la requete est valide
        if ($this->_datas) {
            $this->_isSelected = TRUE;
        } else {
            return FALSE;
        }
    }
    protected function  update() {

        // Variables
        $data = $this->_datas;
        $db = $this->_db;

        // Error
        if (!$data)
            throw new Class_Exception('Update is not possible without data');
        if (!isset($data[self::$_pk_name]))
            throw new Class_Exception('Update is not possible without ID');

        // On forme la requete
        $req = NULL;
        $sql = 'UPDATE ' . self::$_tbl_name . ' SET ';

        foreach ($data as $key => $val)
            if ($key != self::$_pk_name)
                $req .= ', ' . $key . ' = "' . $db->quote($val) . '"';

        $sql .= substr($req, 2, strlen($req));
        $sql .= ' WHERE ' . self::$_pk_name . ' = ?';
        $query = $db->query($sql, $data[self::$_pk_name]);

        // On retourne la valeur de retour de la requete
        return $query;
    }
    protected function  insert() {

        // Varaibles
        $data = $this->_datas;
        $db = $this->_db;

        // Error
        if (!$data)
            throw new Class_Exception('Update is not possible without data');

        // On forme la requete
        $req1 = NULL;
        $req2 = NULL;
        $sql = 'INSERT INTO ' . self::$_tbl_name . ' ';

        foreach ($data as $key => $val)
            if ($key != self::$_pk_name)
                $req1 .= ', ' . $key;
        foreach ($data as $key => $val)
            if ($key != self::$_pk_name)
                $req2 .= ', "' . $db->quote($val) . '"';

        $sql .= '(' . substr($req1, 2, strlen($req1)) . ') VALUES (' . substr($req2, 2, strlen($req2)) . ')';
        $query = $db->query($sql, $data[self::$_pk_name]);

        // Maintenant qu'il est dans la base on peut le passer en SELETED
        $this->_datas[self::$_pk_name] = $db->lastInsertId();
        $this->_isSelected = TRUE;

        // On retourne le resultat de la requete
        return $query;
    }

	// Magic Methodes
    public function     __call($method, $arguments) {

        // On traite la chaine pour obtenir la champs dans la table.
        $l = Utils::firstMajLen($method);
        $params = substr($method, $l, strlen($method));
        $action = substr($method, 0, $l);

        // On crée la methode subsidiaire pour l'action.
        $subMethode = '__call' . strtoupper($action[0]) . substr($action, 1, $l);

        // On regarde si on a définie un methode de regle pour le cette clef.
        if (method_exists($this, $subMethode)) {

            // On appelle la methode subsidiaire.
            return $this->$subMethode($params, $arguments);
        }

        // On retourne Null si ce n'est pas fait.
        return null;

    }
    protected function  __callSetfk($params, $arguments) {

        // // // Regle de travail 'SET FK'
        //
        // $obj = new $obj();
        // $obj->setNomDuChampsDansLaTable('valeur');
        //

        // On formate la string
        $key = 'fk_' . Utils::capsStringToUnderscored($params, TRUE);

        // On a besoin des données de la base pour traiteer la demande
        if ($this->_db == NULL)
            throw new Class_Exception('The link to the database is not set');

        // On set la donnée dans le tableau de datas
        $this->_datas[$key] = $arguments[0];
    }
    protected function  __callSet($params, $arguments) {

        // // // Regle de travail 'SET'
        //
        // $obj = new $obj();
        // $obj->setNomDuChampsDansLaTable('valeur');
        //

        // On formate la string
        $key = self::$_prefix . Utils::capsStringToUnderscored($params, TRUE);

        // On a besoin des données de la base pour traiteer la demande
        if ($this->_db == NULL)
            throw new Class_Exception('The link to the database is not set');

        // On set la donnée dans le tableau de datas
        $this->_datas[$key] = $arguments[0];
    }
    protected function  __callGetfk($params, $arguments) {

        // // // Regle de travail 'GET'
        //
        // $obj = new $obj();
        // $retourne_valuer = $obj->getNomDuChampsDansLaTable();
        //

        // On formate la string
        $key = 'fk_' . Utils::capsStringToUnderscored($params, TRUE);

        // On a besoin des données de la base pour traiteer la demande
        if ($this->_db == NULL)
            throw new Class_Exception('The link to the database is not set');

        // On retourne la donnée demandée.
        if (array_key_exists($key, $this->_datas))
            return $this->_datas[$key];

        // Sinon on retourne NULL
        return null;
    }
    protected function  __callGet($params, $arguments) {

        // // // Regle de travail 'GET'
        //
        // $obj = new $obj();
        // $retourne_valuer = $obj->getNomDuChampsDansLaTable();
        //

        // On formate la string
        $key = self::$_prefix . Utils::capsStringToUnderscored($params, TRUE);

        // On a besoin des données de la base pour traiteer la demande
        if ($this->_db == NULL)
            throw new Class_Exception('The link to the database is not set');

        // On retourne la donnée demandée.
        if (array_key_exists($key, $this->_datas))
            return $this->_datas[$key];

        // Sinon on retourne NULL
        return null;
    }
    public function     __toString() {

        // On formate un retour de base pour un appel sur string.

        // On formate le getteur en fonction de l'indentifiant.
        $tmp = self::$_pk_name;
        $tmp = str_replace(self::$_prefix, '', $tmp);
        $tmp = ucfirst($tmp);
        $tmp = 'get' . $tmp;

        // On retourne la valeur de la clef primaire.
        return $this->$tmp();
    }

}
?>
